import * as PIXI from 'pixi.js';

/**
 * Please note that this is not the most optimal way of doing pure shader generated rendering and should be used when the
 * scene is wanted as input texture. Check the mesh version of example for more performant version if you need only shader
 * generated content.
 **/
const app = new PIXI.Application({ background: '#1099bb', resizeTo: window });

document.body.appendChild(app.view);

PIXI.Assets.load('https://pixijs.com/assets/perlin.jpg').then(onAssetsLoaded);

let filter = null;

const text = new PIXI.Text('PixiJS', { fill: 0xffffff, fontSize: 80 });

text.anchor.set(0.5, 0.5);
text.position.set(app.renderer.screen.width / 2, app.renderer.screen.height / 2);

app.stage.addChild(text);

let totalTime = 0;

// Fragment shader, in real use this would be much cleaner when loaded from a file
// or embedded into the application as data resource.
const fragment = `//Based on this: https://www.shadertoy.com/view/wtlSWX

varying vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform sampler2D noise;
uniform float time;
// Distance function. Just calculates the height (z) from x,y plane with really simple length check.
// Its not exact as there could be shorter distances.
vec2 dist(vec3 p)
{
  float id = floor(p.x)+floor(p.y);
  id = mod(id, 2.);
  float h = texture2D(noise, vec2(p.x, p.y)*0.04).r*5.1;
  float h2 = texture2D(uSampler, vTextureCoord).r;
  return vec2(h+h2-p.z,id);
}
//Light calculation.
vec3 calclight(vec3 p, vec3 rd)
{
  vec2 eps = vec2( 0., 0.001);
  vec3 n = normalize( vec3(
    dist(p+eps.yxx).x - dist(p-eps.yxx).x,
    dist(p+eps.xyx).x - dist(p-eps.xyx).x,
    dist(p+eps.xxy).x - dist(p-eps.xxy).x
  ));

  vec3 d = vec3( max( 0., dot( -rd ,n)));

  return d;
}

void main()
{
  vec2 uv = vec2(vTextureCoord.x, 1.-vTextureCoord.y);
  uv *=2.;
  uv-=1.;

  vec3 cam = vec3(0.,time -2., -3.);
  vec3 target = vec3(sin(time)*0.1, time+cos(time)+2., 0. );
  float fov = 2.2;
  vec3 forward = normalize( target - cam);
  vec3 up = normalize(cross( forward, vec3(0., 1.,0.)));
  vec3 right = normalize( cross( up, forward));
  vec3 raydir = normalize(vec3( uv.x *up + uv.y * right + fov*forward));

  //Do the raymarch
  vec3 col = vec3(0.);
  float t = 0.;
  for( int i = 0; i < 100; i++)
  {
    vec3 p = t * raydir + cam;
    vec2 d = dist(p);
    t+=d.x*0.5;//Jump only half of the distance as height function used is not really the best for heightmaps.
    if(d.x < 0.001)
    {
      vec3 bc = d.y < 0.5 ? vec3(1.0, .8, 0.) :
                vec3(0.8,0.0, 1.0);
      col = vec3( 1.) * calclight(p, raydir) * (1. - t/150.) *bc;
      break;
    }
    if(t > 1000.)
    {
      break;
    }
  }
  gl_FragColor = vec4(col, 1.);
}
`;

function onAssetsLoaded(perlin)
{
    // Add perlin noise for filter, make sure it's wrapping and does not have mipmap.
    perlin.baseTexture.wrapMode = PIXI.WRAP_MODES.REPEAT;
    perlin.baseTexture.mipmap = false;

    // Build the filter
    filter = new PIXI.Filter(null, fragment, {
        time: 0.0,
        noise: perlin,
    });
    app.stage.filterArea = app.renderer.screen;
    app.stage.filters = [filter];

    // Listen for animate update.
    app.ticker.add((delta) =>
    {
        filter.uniforms.time = totalTime;
        totalTime += delta / 60;
    });
}
